package org.lanqiao.music.system.config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.time.Duration;

@SpringBootConfiguration
@EnableCaching
public class RedisConfig {
        @Primary    // 如果IOC中有多个该类型对象时，自动装配首选这个
        @Bean(name = "redisTemplate") // 方法返回值注入IOC，bean id为redisTemplate
        @Resource    // 不加这个注解，入参redisConnectionFactory还是会爆红
        public RedisTemplate<String, Object>
        getRedisTemplate(RedisConnectionFactory redisConnectionFactory) {

            // 创建一个新的redisTemplate对象
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            // 配置redis连接工厂
            redisTemplate.setConnectionFactory(redisConnectionFactory);

            // 创建json序列化处理对象
            GenericJackson2JsonRedisSerializer jsonRedisSerializer =
                    new GenericJackson2JsonRedisSerializer();
            // 创建字符串序列化处理对象  （原样输出）
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

            // key的序列化类型，可以解决key前乱码
            redisTemplate.setKeySerializer(stringRedisSerializer);
            // value的序列化类型，将以上面配置的json格式
            redisTemplate.setValueSerializer(jsonRedisSerializer);
            // hash类型的key配置string序列化
            redisTemplate.setHashKeySerializer(stringRedisSerializer);
            // hash类型的value配置json序列化
            redisTemplate.setHashValueSerializer(jsonRedisSerializer);
            redisTemplate.afterPropertiesSet(); // 在获取配置文件信息之后再设置，已达到完全生效
            return redisTemplate;
        }

    /**
     * 配置缓存管理器
     * @param redisConnectionFactory
     * @return 缓存管理器
     */
    @Bean
    @Resource
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 生成两套默认配置，通过 Config 对象即可对缓存进行自定义配置
        RedisCacheConfiguration cacheConfig1 = RedisCacheConfiguration.defaultCacheConfig()
                // 设置过期时间 10 分钟
                .entryTtl(Duration.ofMinutes(10))
                // 设置key名前缀
                .prefixCacheNameWith("sys:user1:")
                // 禁止缓存 null 值
                .disableCachingNullValues()
                // 设置 key 序列化
                .serializeKeysWith(keyPair())
                // 设置 value 序列化
                .serializeValuesWith(valuePair());

        RedisCacheConfiguration cacheConfig2 = RedisCacheConfiguration.defaultCacheConfig()
                // 设置过期时间 30 秒
                .entryTtl(Duration.ofSeconds(30))
                .prefixCacheNameWith("sys:user2:")//key生成策略
                .disableCachingNullValues()
                .serializeKeysWith(keyPair())
                .serializeValuesWith(valuePair());

        // 返回 Redis 缓存管理器
        return RedisCacheManager.builder(redisConnectionFactory)
                // 将上面的两套配置，注册到缓存管理器中，并定义名称
                .withCacheConfiguration("user1", cacheConfig1)
                .withCacheConfiguration("user2", cacheConfig2)
                .build();
    }

    /**
     * 配置键序列化
     * @return StringRedisSerializer
     */
    private RedisSerializationContext.SerializationPair<String> keyPair() {
        return RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());
    }

    /**
     * 配置值序列化，使用 GenericJackson2JsonRedisSerializer 替换默认序列化
     * @return GenericJackson2JsonRedisSerializer
     */
    private RedisSerializationContext.SerializationPair<Object> valuePair() {
        return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
    }


    /**
     * 创建一个自定义生成key的规则
     */
    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator() {
        // 匿名内部类
        return new KeyGenerator() {
            // target 执行缓存的方法对象，method 执行缓存的方法 params 方法的参数
            @Override
            public Object generate(Object target, Method method, Object... params) {
                // 格式化缓存key字符串
                StringBuilder stringBuilder = new StringBuilder();
                // 追加类名
                stringBuilder.append(target.getClass().getName());
                // 追加方法名
                stringBuilder.append(method.getName());
                // 遍历参数并且追加
                for (Object obj :params) {
                    stringBuilder.append(obj.toString());
                }
                System.out.println("调用Redis缓存Key: " + stringBuilder.toString());
                return stringBuilder.toString();
            }
        };
    }


}
